import base from "./base.js"
import gsCfg from "./gsCfg.js"
import lodash from "lodash"
import fs from "node:fs"
import common from "../../../lib/common/common.js"
import MysInfo from "./mys/mysInfo.js"
import NoteUser from "./mys/NoteUser.js"
import MysUser from "./mys/MysUser.js"
import { promisify } from "node:util"
import YAML from "yaml"
import { Data } from "#miao"
import { Player } from "#miao.models"
import { UserGameDB, sequelize } from "./db/index.js"

export default class User extends base {
  constructor(e) {
    super(e)
    this.model = "bingCk"
    /** 绑定的uid */
    this.uidKey = `Yz:genshin:mys:qq-uid:${this.userId}`

    /** 多角色uid */
    this.allUid = []
    if (this.e.isSr) {
      /** 绑定的uid */
      this.uidKey = `Yz:srJson:mys:qq-uid:${this.userId}`
    }
  }

  // 获取当前user实例
  async user() {
    return await NoteUser.create(this.e)
  }

  async resetCk() {
    let user = await this.user()
    await user.initCache()
  }

  /** 绑定ck */
  async bing() {
    let user = await this.user()
    let set = gsCfg.getConfig("mys", "set")

    if (this.ck && !this.e.ck) {
      this.e.ck = this.ck
    }
    if (!this.e.ck) {
      await this.e.reply(`请【私聊】发送米游社Cookie，获取教程：\n${set.cookieDoc}`)
      return
    }

    let ckStr = this.e.ck.replace(/#|'|"/g, "")
    let param = {}
    ckStr.split(";").forEach(v => {
      // 处理分割特殊cookie_token
      let tmp = lodash.trim(v).replace("=", "~").split("~")
      param[tmp[0]] = tmp[1]
    })

    if (!param.cookie_token && !param.cookie_token_v2) {
      await this.e.reply("发送Cookie不完整\n请退出米游社【重新登录】，刷新完整Cookie")
      return
    }

    // TODO：独立的mys数据，不走缓存ltuid
    let mys = await MysUser.create(
      param.ltuid || param.ltuid_v2 || param.account_id_v2 || param.ltmid_v2,
    )
    if (!mys) {
      await this.e.reply("发送Cookie不完整或数据错误")
      return
    }
    let data = {}
    data.ck = `ltoken=${param.ltoken};ltuid=${param.ltuid || param.login_uid};cookie_token=${param.cookie_token || param.cookie_token_v2}; account_id=${param.ltuid || param.login_uid};`
    let flagV2 = false

    if (param.cookie_token_v2 && (param.account_mid_v2 || param.ltmid_v2)) {
      //
      // account_mid_v2 为版本必须带的字段，不带的话会一直提示绑定cookie失败 请重新登录
      flagV2 = true
      data.ck = `ltuid=${param.ltuid || param.login_uid || param.ltuid_v2};account_mid_v2=${param.account_mid_v2};cookie_token_v2=${param.cookie_token_v2};ltoken_v2=${param.ltoken_v2};ltmid_v2=${param.ltmid_v2};`
    }
    if (param.mi18nLang) {
      data.ck += ` mi18nLang=${param.mi18nLang};`
    }
    /** 拼接ck */
    data.ltuid = param.ltuid || param.ltuid_v2 || param.account_id_v2 || param.ltmid_v2

    /** 米游币签到字段 */
    data.login_ticket = param.login_ticket ?? ""

    mys.setCkData(data)

    /** 检查ck是否失效 */
    let uidRet = await mys.reqMysUid()
    if (uidRet.status !== 0) {
      logger.mark(`绑定Cookie错误1：${this.checkMsg || "Cookie错误"}`)
      // 清除mys数据
      mys._delCache()
      return await this.e.reply(`绑定Cookie失败：${this.checkMsg || "Cookie错误"}`)
    }

    // 判断data.ltuid是否是数字
    if (flagV2 && isNaN(data.ltuid)) {
      // 获取米游社通行证id
      let userFullInfo = await mys.getUserFullInfo()
      if (userFullInfo?.data?.user_info) {
        let userInfo = userFullInfo?.data?.user_info
        this.ltuid = userInfo.uid || this.ltuid
        this.ck = `${this.ck}ltuid=${this.ltuid};`
      } else {
        logger.mark(`绑定Cookie错误2：${userFullInfo.message || "Cookie错误"}`)
        return await this.e.reply(`绑定Cookie失败：${userFullInfo.message || "Cookie错误"}`)
      }
    }

    logger.mark(`${this.e.logFnc} 检查Cookie正常 [ltuid:${mys.ltuid}]`)

    await user.addMysUser(mys)
    await mys.initCache()
    await user.save()

    logger.mark(`${this.e.logFnc} 保存Cookie成功 [ltuid:${mys.ltuid}]`)

    let uidMsg = ["绑定Cookie成功", mys.getUidInfo()]
    await this.e.reply(uidMsg.join("\n"))
    let msg = []
    let button = []
    if (mys.hasGame("gs")) {
      msg.push(
        "原神模块支持：",
        "【#uid】当前绑定ck uid列表",
        "【#我的ck】查看当前绑定ck",
        "【#删除ck】删除当前绑定ck",
        "【#体力】查询当前树脂",
        "【#原石】查看原石札记",
        "【#原石统计】原石统计数据",
        "【#练度统计】角色列表数据",
        "【#面板】【#更新面板】面板信息",
      )
      button.push(
        [
          { text: "#uid", callback: "#uid" },
          { text: "#我的ck", callback: "#我的ck" },
          { text: "#删除ck", callback: "#删除ck" },
        ],
        [
          { text: "#体力", callback: "#体力" },
          { text: "#原石", callback: "#原石" },
          { text: "#原石统计", callback: "#原石统计" },
        ],
        [
          { text: "#练度统计", callback: "#练度统计" },
          { text: "#面板", callback: "#面板" },
          { text: "#更新面板", callback: "#更新面板" },
        ],
      )
    }
    if (mys.hasGame("sr")) {
      msg.push(
        "星穹铁道支持：",
        "【*uid】当前绑定ck uid列表",
        "【*删除ck】删除当前绑定ck",
        "【*体力】查询当前开拓力",
        "【*星琼】查看星琼月历",
        "【*星琼统计】星琼统计数据",
        "【*练度统计】角色列表数据",
        "【*面板】【*更新面板】面板信息",
      )
      button.push(
        [
          { text: "*uid", callback: "*uid" },
          { text: "*删除ck", callback: "*删除ck" },
          { text: "*体力", callback: "*体力" },
        ],
        [
          { text: "*星琼", callback: "*星琼" },
          { text: "*星琼统计", callback: "*星琼统计" },
          { text: "*练度统计", callback: "*练度统计" },
        ],
        [
          { text: "*面板", callback: "*面板" },
          { text: "*更新面板", callback: "*更新面板" },
        ],
      )
    }
    if (mys.hasGame("zzz")) {
      msg.push("绝区零支持：", "无")
    }
    msg = await common.makeForwardMsg(
      this.e,
      [[msg.join("\n"), segment.button(...button)]],
      "绑定成功：使用命令说明",
    )
    await this.e.reply(msg)
  }

  /** 删除绑定ck */
  async delCk() {
    let game
    if (this.e.game) {
      game = this.e.game
    } else {
      game = "gs"
    }
    // 判断是原神还是星铁
    let user = await this.user()
    // 获取当前uid
    let uidData = user.getUidData("", (game = game), this.e)
    if (!uidData || uidData.type !== "ck" || !uidData.ltuid) {
      return `删除失败：当前的UID${uidData?.uid}无CK信息`
    }
    let mys = await MysUser.create(uidData.ltuid)
    if (!mys) {
      return `删除失败：当前的UID${uidData?.uid}无CK信息`
    }
    let msg = ["绑定Cookie已删除", mys.getUidInfo()]
    await user.delMysUser(uidData.ltuid)
    return msg.join("\n")
  }

  /** 绑定uid，若有ck的话优先使用ck-uid */
  async bingUid() {
    let uid = this.e.msg.match(
      this.e.game == "zzz" ? /(1[0-9]|[1-9])[0-9]{8}|[1-9][0-9]{7}/g : /(18|[1-9])[0-9]{8}/g,
    )
    if (!uid) return
    uid = uid[0]
    let user = await this.user()
    await user.addRegUid(uid, this.e)
    return await this.showUid()
  }

  async delUid(index) {
    let user = await this.user()
    let game = this.e
    let uidList = user.getUidList(game)
    if (index > uidList.length) {
      return await this.e.reply([
        "uid序号输入错误",
        segment.button([{ text: "删除uid", input: "#删除uid" }]),
      ])
    }
    index = Number(index) - 1
    let uidObj = uidList[index]
    if (uidObj.type === "ck") {
      return await this.e.reply([
        "CK对应UID无法直接删除，请通过【#删除ck】命令来删除",
        segment.button([{ text: "删除ck", callback: "#删除ck" }]),
      ])
    }
    await user.delRegUid(uidObj.uid, game)
    return await this.showUid()
  }

  /** #uid */
  async showUid_bak() {
    let user = await this.user()
    let msg = []
    let typeMap = { ck: "CK Uid", reg: "绑定 Uid" }
    lodash.forEach({ gs: "原神 (#uid)", sr: "星穹铁道 (*uid)" }, (gameName, game) => {
      let uidList = user.getUidList(game)
      let currUid = user.getUid(game)
      msg.push(`【${gameName}】`)
      if (uidList.length === 0) {
        msg.push(`暂无，通过${game === "gs" ? "#" : "*"}绑定123456789来绑定UID`)
        return true
      }
      lodash.forEach(uidList, (ds, idx) => {
        let tmp = `${++idx}: ${ds.uid} (${typeMap[ds.type]})`
        if (currUid * 1 === ds.uid * 1) {
          tmp += " ☑"
        }
        msg.push(tmp)
      })
    })
    msg.unshift("通过【#uid+序号】来切换uid，【#删除uid+序号】删除uid")
    await this.e.reply(msg.join("\n"))
  }

  /** #uid */
  async showUid() {
    let user = await this.user()
    let uids = [
      {
        key: "gs",
        name: "原神",
      },
      {
        key: "sr",
        name: "星穹铁道",
      },
      {
        key: "zzz",
        name: "绝区零",
      },
    ]
    lodash.forEach(uids, ds => {
      ds.uidList = user.getUidList(ds.key)
      ds.uid = user.getUid(ds.key)
      lodash.forEach(ds.uidList, uidDs => {
        if (ds.key !== "zzz") {
          let player = Player.create(uidDs.uid, ds.key)
          if (player) {
            uidDs.name = player.name
            uidDs.level = player.level
            let imgs = player?.faceImgs || {}
            uidDs.face = imgs.face
            uidDs.banner = imgs.banner
          }
        } else {
          uidDs.zzz_face = true
          uidDs.zzz_banner = true
        }
      })
    })
    return this.e.reply([
      await this.e.runtime.render("genshin", "html/user/uid-list", { uids }, { retType: "base64" }),
      segment.button(
        [
          { text: "绑定UID", input: "#绑定uid" },
          { text: "切换UID", input: "#uid" },
          { text: "删除UID", input: "#删除uid" },
        ],
        [
          { text: "角色", callback: "#角色" },
          { text: "探索", callback: "#探索" },
          { text: "武器", callback: "#武器" },
          { text: "深渊", callback: "#深渊" },
        ],
        [
          { text: "统计", callback: "#练度统计" },
          { text: "面板", callback: "#面板" },
          { text: "体力", callback: "#体力" },
          { text: "原石", callback: "#原石" },
        ],
        [
          { text: "留影", callback: "#留影叙佳期" },
          { text: "七圣", callback: "#七圣召唤查询牌组" },
          { text: "抽卡", callback: "#抽卡记录" },
          { text: "充值", callback: "#充值记录" },
        ],
      ),
    ])
  }

  /** 切换uid */
  async toggleUid(index) {
    let user = await this.user()
    let game = this.e.game || "gs"
    let uidList = user.getUidList(game)
    if (index > uidList.length) {
      return await this.e.reply([
        "uid序号输入错误",
        segment.button([{ text: "切换uid", input: "#uid" }]),
      ])
    }
    index = Number(index) - 1
    user.setMainUid(index, game)
    await user.save()
    return await this.showUid()
  }

  /** 加载V2ck */
  async loadOldDataV2() {
    let file = [
      "./data/MysCookie/NoteCookie.json",
      "./data/NoteCookie/NoteCookie.json",
      "./data/NoteCookie.json",
    ]
    let json = file.find(v => fs.existsSync(v))
    if (!json) return

    let list = JSON.parse(fs.readFileSync(json, "utf8"))
    let arr = {}

    logger.mark(logger.green("加载用户ck..."))

    lodash.forEach(list, (ck, qq) => {
      if (ck.qq) qq = ck.qq

      let isMain = false
      if (!arr[qq]) {
        arr[qq] = {}
        isMain = true
      }

      let param = {}
      ck.cookie.split(";").forEach(v => {
        let tmp = lodash.trim(v).split("=")
        param[tmp[0]] = tmp[1]
      })

      let ltuid = param.ltuid

      if (!param.cookie_token) return

      arr[qq][String(ck.uid)] = {
        uid: ck.uid,
        qq,
        ck: ck.cookie,
        ltuid,
        isMain,
      }
    })

    let count = await this.loadOldData(arr)
    if (count > 0) {
      logger.mark(logger.green(`DB导入V2用户ck${count}个`))
    }
    fs.unlinkSync(json)
  }

  /** 加载V3ck */
  async loadOldDataV3() {
    let dir = "./data/MysCookie/"
    if (!fs.existsSync(dir)) {
      return false
    }
    Data.createDir("./temp/MysCookieBak")
    let files = fs.readdirSync(dir).filter(file => file.endsWith(".yaml"))
    const readFile = promisify(fs.readFile)
    let promises = []
    if (files.length === 0) {
      fs.rmdirSync("./data/MysCookie/")
      return
    }
    files.forEach(v => promises.push(readFile(`${dir}${v}`, "utf8")))
    const res = await Promise.all(promises)
    let ret = {}
    for (let v of res) {
      v = YAML.parse(v)
      let qq
      for (let k in v) {
        qq = qq || v[k]?.qq
      }
      if (qq) {
        ret[qq] = v
      }
    }
    let count = await this.loadOldData(ret)
    if (count > 0) {
      logger.mark(logger.green(`DB导入V3用户ck${count}个`))
    }
  }

  async loadOldUid() {
    // 从DB中导入
    await sequelize.query("delete from UserGames where userId is null or data is null", {})
    let games = await UserGameDB.findAll()
    let count = 0
    await Data.forEach(games, async game => {
      if (!game.userId) {
        game.destroy()
        return true
      }
      count++
      let user = await NoteUser.create(game.userId)
      if (game.userId && game.data) {
        lodash.forEach(game.data, ds => {
          let { uid } = ds
          user.addRegUid(uid, game.game, false)
        })
      }
      if (game.uid) {
        user.setMainUid(game.uid, game.game, false)
      }
      await user.save()
      await game.destroy()
    })

    // 从Redis中导入
    let keys = await redis.keys("Yz:genshin:mys:qq-uid:*")
    for (let key of keys) {
      let uid = await redis.get(key)
      let qqRet = /Yz:genshin:mys:qq-uid:(\d{5,12})/.exec(key)
      if (qqRet?.[1] && uid) {
        let user = await NoteUser.create(qqRet[1])
        if (!user.getUid("gs")) {
          user.addRegUid(uid, "gs")
        }
      }
      redis.del(key)
    }
    await sequelize.query(
      "delete from Users where (ltuids is null or ltuids='') and games is null",
      {},
    )
    console.log("load Uid Data Done...")
  }

  async loadOldData(data) {
    let count = 0
    if (!lodash.isPlainObject(data)) {
      return
    }
    for (let u in data) {
      let ltuids = {}
      let v = data[u]
      let qq
      for (let k in v) {
        let data = v[k]
        qq = qq || data?.qq
        let { uid, ck, ltuid, region_name: region, device_id: device } = data
        ltuids[ltuid] = ltuids[ltuid] || {
          ck,
          device,
          ltuid,
          uids: {},
          type: /America Server|Europe Server|Asia Server/.test(region) ? "hoyolab" : "mys",
        }
        let tmp = ltuids[ltuid]
        let game = region === "星穹列车" ? "sr" : "gs"
        tmp.uids[game] = tmp.uids[game] || []
        let gameUids = tmp.uids[game]
        if (!gameUids.includes(uid + "")) {
          gameUids.push(uid + "")
        }
      }
      if (!qq) {
        continue
      }
      let user = await NoteUser.create(qq)
      for (let ltuid in ltuids) {
        let data = ltuids[ltuid]
        let mys = await MysUser.create(data.ltuid)
        if (mys) {
          mys.setCkData(data)
          await mys.save()
          user.addMysUser(mys)
        }
      }
      await user.save()
      if (fs.existsSync(`./data/MysCookie/${qq}.yaml`)) {
        try {
          let src = `./data/MysCookie/${qq}.yaml`
          let dest = `./temp/MysCookieBak/${qq}.yaml`
          await fs.promises.unlink(dest).catch(_ => {})
          await fs.promises.copyFile(src, dest)
          await fs.promises.unlink(src)
        } catch (err) {
          console.log(err)
        }
      }
      count++
    }
  }

  /** 我的ck */
  async myCk() {
    let user = await this.user()
    if (!user.hasCk) {
      this.e.reply(["当前尚未绑定Cookie", segment.button([{ text: "帮助", input: "#Cookie帮助" }])])
    }
    let mys = user.getMysUser(this.e)
    if (mys) {
      await this.e.reply(`当前绑定Cookie\nuid：${mys.getUid(this.e)}`)
      await this.e.reply(mys.ck)
    }
  }

  async checkCkStatus() {
    let user = await this.user()
    if (!user.hasCk) {
      await this.e.reply(`\n未绑定CK，当前绑定uid：${user.uid || "无"}`, false, { at: true })
      return true
    }
    let uid = user.uid * 1
    let uids = user.ckUids

    let checkRet = await user.checkCk()
    let cks = []
    lodash.forEach(checkRet, (ds, idx) => {
      let tmp = [`\n#${idx + 1}: [CK:${ds.ltuid}] - 【${ds.status === 0 ? "正常" : "失效"}】`]
      if (ds.uids && ds.uids.length > 0) {
        let dsUids = []
        lodash.forEach(ds.uids, u => {
          dsUids.push(u * 1 === uid ? `☑${u}` : u)
        })
        tmp.push(`绑定UID: [ ${dsUids.join(", ")} ]`)
      }
      if (ds.status !== 0) {
        tmp.push(ds.msg)
      }
      cks.push(tmp.join("\n"))
    })
    if (uids.length > 1) {
      cks.push(`当前生效uid：${uid}\n通过【#uid】命令可查看并切换UID`)
    }

    await this.e.reply(
      [
        cks.join("\n----\n"),
        segment.button([
          { text: "绑定UID", input: "#绑定uid" },
          { text: "切换UID", input: "#uid" },
          { text: "删除UID", input: "#删除uid" },
        ]),
      ],
      false,
      { at: true },
    )
  }

  async userAdmin() {
    this.model = "userAdmin"
    await MysInfo.initCache()
    let stat = await MysUser.getStatData()
    return {
      saveId: "user-admin",
      ...stat,
      _plugin: "genshin",
      ...this.screenData,
    }
  }

  async bindNoteUser() {
    let user = await this.user()
    let id = user.qq
    let { e } = this
    let { msg, mainUserId, originalUserId } = e
    if (!id) {
      return true
    }
    if (/(删除绑定|取消绑定|解除绑定|解绑|删除|取消)/.test(msg)) {
      // 删除用户
      id = originalUserId || id
      if (/主/.test(msg)) {
        let mainId = await redis.get(`Yz:NoteUser:mainId:${id}`)
        if (!mainId) {
          e.reply("当前用户没有主用户，在主用户中通过【#绑定用户】可进行绑定...")
          return true
        }
        let subIds = await Data.getCacheJSON(`Yz:NoteUser:subIds:${mainId}`)
        delete subIds[id]
        await redis.del(`Yz:NoteUser:mainId:${id}`)
        await Data.setCacheJSON(`Yz:NoteUser:subIds:${mainId}`, subIds)
        e.reply("已经解除与主用户的绑定...")
      } else if (/子/.test(msg)) {
        let subIds = await Data.getCacheJSON(`Yz:NoteUser:subIds:${id}`)
        let count = 0
        for (let key in subIds) {
          await redis.del(`Yz:NoteUser:mainId:${key}`)
          count++
        }
        if (count > 0) {
          e.reply(`已删除${count}个子用户...`)
          await redis.del(`Yz:NoteUser:subIds:${id}`)
        } else {
          e.reply("当前用户没有子用户，通过【#绑定用户】可绑定子用户...")
        }
      }
      return true
    }
    msg = msg.replace(/^#\s*(接受)?绑定(主|子)?(用户|账户|账号)/, "")
    let idRet = /^\[([a-zA-Z0-9-]{5,})](?:\[([a-zA-Z0-9-]+)])?$/.exec(msg)
    if (idRet && idRet[1]) {
      let mainId = idRet[1]
      let currId = id.toString()
      if (!idRet[2]) {
        // 子用户绑定
        if (currId === mainId) {
          if (originalUserId && originalUserId !== mainId && mainUserId === mainId) {
            e.reply("当前账户已完成绑定...")
          } else {
            e.reply("请切换到需要绑定的子用户并发送绑定命令...")
          }
          return true
        }
        let verify = Math.floor(100000000 + Math.random() * 100000000).toString()
        await redis.set(`Yz:NoteUser:verify:${mainId}`, verify + "||" + currId, { EX: 300 })
        e.reply(
          [
            `此账号将作为子用户，绑定至主用户:${mainId}`,
            "成功绑定后，此用户输入的命令，将视作主用户命令，使用主用户的CK与UID等信息",
            "如需继续绑定，请在5分钟内，使用主账户发送以下命令：",
            "",
            `#接受绑定子用户[${mainId}][${verify}]`,
          ].join("\n"),
        )
        return true
      } else {
        // 接受绑定
        if (currId !== mainId) {
          e.reply("请切换到主用户并发送接受绑定的命令...")
          return true
        }
        let verify = (await redis.get(`Yz:NoteUser:verify:${mainId}`)) || ""
        verify = verify.split("||")
        if (!verify || verify[0] !== idRet[2] || !verify[1]) {
          e.reply("校验失败，请发送【#绑定用户】重新开始绑定流程")
          await redis.del(`Yz:NoteUser:verify:${mainId}`)
          return true
        }
        let subId = verify[1]
        await redis.del(`Yz:NoteUser:verify:${mainId}`)
        await redis.set(`Yz:NoteUser:mainId:${subId}`, mainId, { EX: 3600 * 24 * 365 })
        let subIds = await Data.getCacheJSON(`Yz:NoteUser:subIds:${mainId}`)
        subIds[subId] = "true"
        await Data.setCacheJSON(`Yz:NoteUser:subIds:${mainId}`, subIds)
        e.reply(
          "绑定成功，绑定的子用户可使用主用户的UID/CK等信息\n请勿接受不是自己用户的绑定，如需解绑可通过【#解绑子用户】进行解绑",
        )
        return true
      }
    } else {
      if (mainUserId && originalUserId && originalUserId !== mainUserId) {
        e.reply("当前账户已有绑定的主账户，请使用主账户发起绑定...")
        return true
      }
      e.reply(
        [
          "将此账号作为【主用户】，绑定其他子用户。",
          "",
          "可在多个QQ或频道间打通用户信息，子用户会使用主用户的CK与UID等信息",
          "注意：请勿接受不是自己用户的绑定！",
          "请【切换至需要绑定的子用户】并发送以下命令，获得验证命令...",
          "",
          `#绑定主用户[${id}]`,
        ].join("\n"),
      )
    }
  }
}
